package com.boot.netty.commons.dispatcher;

import com.boot.netty.commons.entity.BaseObjectInvoke;
import com.boot.netty.commons.entity.NullWritable;
import com.boot.netty.commons.exception.NettyException;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * <p>Title:NettyBeanDispatcher</p>
 * <p>Description:netty对象分配器</p>
 * @version V1.0
 * @author ZhiPeng_Chen
 * @date: 2018/11/13
 */
@Slf4j
@Component
public class NettyBeanDispatcher implements ApplicationContextAware {

    
    private ApplicationContext applicationContext;

    /**
     *
     * 远程调用的服务信息
     *
     * @author ZhiPeng_Chen
     * @param channelHandlerContext
     * @param invokeMeta
     */
    public void dispatcher(final ChannelHandlerContext channelHandlerContext, final BaseObjectInvoke invokeMeta) {
        ChannelFuture channelFuture = null;
        Class<?> interfaceClass = invokeMeta.getInterfaceClass();
        String name = invokeMeta.getMethodName();
        Object[] args = invokeMeta.getArgs();
        Class<?>[] parameterTypes = invokeMeta.getParameterTypes();
        Object targetObject = this.applicationContext.getBean(interfaceClass);
        Method method;
        try {
            method = targetObject.getClass().getMethod(name, parameterTypes);
        } catch (NoSuchMethodException e) {
            log.error("[获取远程方法异常] {}", e.getMessage());
            channelFuture = channelHandlerContext.writeAndFlush(e);
            return;
        } finally {
            if (channelFuture != null) {
                channelFuture.addListener(ChannelFutureListener.CLOSE);
            }
        }
        try {
            Object result = method.invoke(targetObject, args);
            if (result == null) {
                log.info("{} -> [方法没有返回值，返回NullWritable对象]", this.getClass().getName());
                channelFuture = channelHandlerContext.writeAndFlush(NullWritable.nullWritable());
            } else {
                log.info("{} -> [返回结果] {}", this.getClass().getName(), result);
                channelFuture = channelHandlerContext.writeAndFlush(result);
            }
            /*
            虽然可以通过ChannelFuture的get()方法获取异步操作的结果,但完成时间是无法预测的,若不设置超时时间则有可能导致线程长时间被阻塞;
            若是不能精确的设置超时时间则可能导致I/O操作中断.
            因此,Netty建议通过GenericFutureListener接口执行异步操作结束后的回调.
             */
//             ChannelFutureListener.CLOSE = new ChannelFutureListener() {
//                @Override
//                public void operationComplete(ChannelFuture future) {
//                    future.channel().close();
//                }
//             };
        } catch (Exception e) {
            Throwable targetException = ((InvocationTargetException) e).getTargetException();
            log.error("{} -> [方法执行异常] {}", this.getClass().getName());
            if (targetException instanceof NettyException) {
                log.error("{} -> [异常信息] {}", this.getClass().getName(), targetException.getMessage());
            }
            channelFuture = channelHandlerContext.writeAndFlush(targetException);
        } finally {
            if (channelFuture != null) {
                channelFuture.addListener(ChannelFutureListener.CLOSE);
            }
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;

    }
}
